#include "perseusHTTP.h"
#include "multipart_formdata.c"
#include <stdio.h>
#include <string.h>

#define MY_FREE(p) \
        free((p)); \
        (p) = NULL;

/*
 * Perseus data are only :
 * A B C D E F
 * 0 1 2 3 4 5 6 7 8 9
 * 
 */

unsigned char
testPerseusByte(char bytes)
{
	unsigned char ret = 0;

	if ( 'A'<=bytes && bytes<='F')
	{
		ret =1;
		goto end;
	}
	
	if ( '0'<=bytes && bytes<='9')
	{
		ret =1;
		goto end;
	}
end:
	return ret;	
}
/*
inline void MY_FREE( void *p)
{
        free(p);
        p=NULL;
}*/
/* Find the PERSEUS_SEP in request
 * if found : return 1 and PerseusSEPposition = Separator position 
 * else :     return 0
 */
unsigned char 
findPerseusSEP(char * request, unsigned long requestLength,
		unsigned long * PerseusSEPposition)
{
	unsigned long i=0;
	unsigned long j=0;
	unsigned char ret = 0;
	char sep[]=PERSEUS_SEP;


	*PerseusSEPposition=0;

	while ( i< requestLength  
		&& request[i] != '\0'
		&& j< PERSEUS_SEP_LENGTH
	      )
	{    
		if (request[i] != sep[j])
		{    
			/* May-be the next byte is the boundary*/
			*PerseusSEPposition=i+1;
			j=0;
			if (testPerseusByte(request[i]) == 0)
			{
				ret = 0;
				goto end;
			}
		}
		else 
		{
			j++;
		}
		i++;
	}

	if (j < PERSEUS_SEP_LENGTH )
	{
		ret = 0;
		goto end;
	}

	ret = 1;

end:
	return ret;
}

char *
addData(char ** res, unsigned long *resLength,
        char * data, unsigned long dataLength)
{
    char * tmp = NULL;
    *res = (char *) realloc(*res,sizeof(char) *( (*resLength) + dataLength+1));
    if (*res==NULL)
    {
        fprintf(stderr,"Error no Enought Memory \n");
        *res = NULL;
        goto end;
    }
    tmp = memcpy((*res)+(*resLength),data,dataLength);
    if (tmp == NULL)
    {
       MY_FREE(*res); 
        *res = NULL;
        goto end;
    }
    (*resLength) = (*resLength) + dataLength; 
    (*res)[(*resLength)]='\0';
    
end: 
    return *res;
}

/*
 * Search in data PerseusSEPs and 
 * decode the parts founds
 * return datadecoded and the rest of data encoded
 * return 1 all is decoded
 * return 2 data rest
 * return 0 error
 */

unsigned char
perseusHTTPdecode(pcc * code,
		  char * request, unsigned long  requestLength,
		  char ** dataDecoded, unsigned long *dataDecodedLength,
		  unsigned long * dataRest)
{
	unsigned char ret = 0;
	unsigned long position =0;
	char * dataDecodedTmp =NULL;
	unsigned long dataDecodedTmpLength =0;
	char * cursor = request;
	unsigned long cursorLength = requestLength;
	*dataRest = 0;
    
    if (requestLength ==0)
    {
        *dataDecoded = addData(dataDecoded,dataDecodedLength,
                        "", 0);
        ret = 1;
        goto end;
    }

	while (findPerseusSEP(cursor,cursorLength,&position) == 1)
	{
		ret = pcc_decode(code,cursor,position,
				&dataDecodedTmp,&dataDecodedTmpLength);
		
		if (ret == 0)
		{
			goto end1;
		}
		*dataDecoded = addData(dataDecoded, dataDecodedLength,
				dataDecodedTmp, dataDecodedTmpLength);
		MY_FREE(dataDecodedTmp);
		dataDecodedTmpLength = 0;
		if (*dataDecoded == NULL)
		{
			ret =0;
			goto end;
		}
		cursor = cursor + position + PERSEUS_SEP_LENGTH;
		cursorLength = cursorLength - position -PERSEUS_SEP_LENGTH; 
		*dataRest +=position+PERSEUS_SEP_LENGTH;
		position = 0;
	}
	
	if (*dataRest == requestLength)
	{
		ret = 1;
	}
	else
	{
		ret =2;
	}
	goto end;
end1:
	MY_FREE(dataDecoded);
end:
	return ret;
}
	

unsigned char
perseusHTTPencode(pcc * code, char * data, unsigned long dataLength,
                    char ** dataEncoded, unsigned long *dataEncodedLength)
{

    unsigned char ret = 0;
    unsigned int partLength =0;
    unsigned int partNbr = 0;
    unsigned int i = 0;
    unsigned int lastpartLength = 0;
    char * dataEncodedTmp = NULL;
    unsigned long dataEncodedTmpLength=0;

    if (dataLength ==0)
    {
        *dataEncoded = addData(dataEncoded,dataEncodedLength,
                        "", 0);
        ret = 1;
        goto end;
    }

    /* 
     * Cut the data in PERSEUS_MAX_LENGTH* k/n
     */

    partLength = PERSEUS_MAX_LENGTH * code->mK/(code->mN*2);
    partNbr = dataLength / partLength; 
    lastpartLength = dataLength - (partNbr * partLength);
    while (partNbr>0)
    {

        /* Encode */
        ret = pcc_Code(code,
                    data+(partLength * i),
                    partLength,
                    &dataEncodedTmp,
                    &dataEncodedTmpLength);

        if (ret==0)
        {
            fprintf(stderr,"CRitical Error in pcc_code! Die!\n");
            MY_FREE(*dataEncoded);
            goto end;
        }
        /* Add in the buffer*/
        *dataEncoded = addData(dataEncoded,dataEncodedLength,
                        dataEncodedTmp, dataEncodedTmpLength);
        MY_FREE(dataEncodedTmp);
        dataEncodedTmpLength = 0;
        if (*dataEncoded == NULL)
        {
            ret = 0;
            goto end;
        }
        /* Add the PERSEUS_SEP*/
        *dataEncoded = addData(dataEncoded,dataEncodedLength,
                PERSEUS_SEP, strlen(PERSEUS_SEP));
        if (*dataEncoded == NULL)
        {
            ret = 0;
            goto end;
        }
        i++;
        partNbr--;
    }

        /* Process the last part */

        /* Encode */
        ret = pcc_Code(code,
                    data+(partLength * i),
                    lastpartLength,
                    &dataEncodedTmp,
                    &dataEncodedTmpLength);

        if (ret==0)
        {
            fprintf(stderr,"CRitical Error in pcc_code! Die!\n");
            MY_FREE(*dataEncoded);
            goto end;
        }
        /* Add in the buffer*/
        *dataEncoded = addData(dataEncoded,dataEncodedLength,
                        dataEncodedTmp, dataEncodedTmpLength);
        MY_FREE(dataEncodedTmp);
        dataEncodedTmpLength = 0;
        if (*dataEncoded == NULL)
        {
            ret = 0;
            goto end;
        }
        /* Add the PERSEUS_SEP*/
        *dataEncoded = addData(dataEncoded,dataEncodedLength,
                PERSEUS_SEP, strlen(PERSEUS_SEP));
        if (*dataEncoded == NULL)
        {
            ret = 0;
            goto end;
        }
end:
    return ret;
}

unsigned char
gotoEndtheLine(char ** data, unsigned long * length)
{
    while ( (*length>0) 
            && ((*data)!=NULL) 
            && ((**data) != '\r')
          )
    {
        (*data)++;
        (*length)--;
    }

    (*data)++;
    (*length)--;

    if (   ((*length) ==0 )
            || ((*data)==NULL)
            || ((**data) != '\n')
       )
    {
        return 0;
    }
    (*data)++;
    (*length)--;
    
    return 1;
}

char * 
getMultipartData(char ** data, unsigned long *length, 
				 char *boundary, unsigned long boundaryLength,
				 unsigned long *resultLength)
{
    unsigned long i =0;
    char * result ;
    char * begin = *data;
    char * boundtmp = *data;
    
	*resultLength = 0;

    /* 
     * Search the boundary in the data
     */
    while ( (*length>0) 
            && ((*data)!=NULL) 
            && (i<boundaryLength)
          )
    {
		/*
         * printf("[%lu:%lu] %u ",i,*length,(unsigned char)**data);
         */
       if (**data != boundary[i])
       {    
            /* May-be the next byte is the boundary*/
            boundtmp = *data+1;
            i=0;
       }
       else
	   {        
		   i++;
	   }
       (*data)++;
       (*length)--;
    }

    /* 
     * Boundary not found !
     */
    if (i != boundaryLength)
    {
        result = NULL;
        goto end;
    }

    /* 
     * 2 bytes  before boundary are : \r\n
     */
    boundtmp -=2;
    /*Save The Data */
   
    *resultLength =(unsigned long) (boundtmp - begin);
    result = (char *) malloc(sizeof(char) * (*resultLength+1));
    
    if (result == NULL)
    {
        goto end;
    }

    result = memcpy((void *) result,(void *) begin, *resultLength);
    result[*resultLength]= '\0';
end:
    return result;
}



/*
 * C++ version 0.4 char* style "itoa2":
 * Written by Lukás Chmela
 * Released under GPLv3.
 */
char * 
itoa2(int value, char* result, int base)
{
    /* check that the base if valid*/
    char * ptr = result, *ptr1 = result, tmp_char;
    int tmp_value;

    if (base < 2 || base > 36) 
    {
        *result = '\0'; 
        return result;
    }

    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
    } while ( value );
    
    /* Apply negative sign*/
    if (tmp_value < 0) 
        *ptr++ = '-';
    
    *ptr-- = '\0';
    while(ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr1;
        *ptr1++ = tmp_char;
    }

    return result;
}

unsigned char
replaceLength(char **resHead, unsigned long *resHeadLength,unsigned long bodyLength)
{
    char * bodyLengthString = NULL;
    unsigned long bodyLengthStringLength = 0;
    unsigned long i = 0;
    char ret = 0;

    bodyLengthString = (char *) malloc(sizeof(char)*256);
    if (bodyLengthString == NULL)
    {
        ret =0;
        goto end;
    }
    bodyLengthString = itoa2(bodyLength,bodyLengthString,10);
    bodyLengthStringLength = strlen(bodyLengthString);

    /*
     *Search back in resHead ':'
     */
    i = *resHeadLength-1;
    while ( (i>0) && (((*resHead)[i]) != ':'))
    {
        i--;
    }
    
    if (i==0)
    {
        ret = 0;
        goto end1;
    }
    
    /* 
     * The next car after ':' is a blank
     */
    i+=2;
    /*
     * Truncate the data!!
     */
    (*resHead)[i]='\0';
    *resHeadLength = i;
    /*
     * Calculate the length of the old length :s
     * -2 : is \r\n at the end of the line
     */
     *resHead = addData(resHead,resHeadLength,bodyLengthString,bodyLengthStringLength);
     if (*resHead == NULL)
     {
         ret = 0;
         goto end1;
     }
     *resHead = addData(resHead,resHeadLength,"\r\n\r\n",4);
     if (*resHead == NULL)
     {
         ret = 0;
         goto end1;
     }
     
     ret = 1;
end1: 
   MY_FREE(bodyLengthString);
end:
    return ret;

}

char *
parseMultipart(char * body, unsigned long length, pcc * PerseusParam, PERSEUS_MODE_t mode)
{
    unsigned char ret=0;
    char * boundary = NULL;
    char * begin = NULL;
    unsigned long boundaryLength = 0;
    char * data = NULL; 
    char * resBody = NULL;
    char * resHead = NULL;
    char * res = NULL;
    unsigned long resBodyLength = 0;
    unsigned long resHeadLength = 0;
    unsigned long dataLength = 0; 
    unsigned long headLength = 0;
	char * dataPerseus = NULL;
    unsigned long dataPerseusLength = 0;


    if (length==0)
    {
        res = NULL;
        goto end;
    }

    /*
     * The 3 first lines are  the header
     *   1 : Content-Type ... SKIP IT! We know it's Multipart/form-data
     *   2 : Length : ....... We Must replace the length 
     *   3 : /r/n ............SKIP IT!
     */
    /* First line */
    begin = body; 
    ret = gotoEndtheLine(&body,&length);
    if (ret==0)
    {
        fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
        res = NULL;
        goto end;
    }

    resHead = addData(&resHead,&resHeadLength,begin,body-begin);
    if (resHead == NULL)
    {
        res = NULL;
        goto end;
    }
    /*
     * The second Line we save it
     */
    begin = body; 
    ret = gotoEndtheLine(&body,&length);
    if (ret==0)
    {
        fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
        res = NULL;
        goto end1;
    }

    resHead = addData(&resHead,&resHeadLength,begin,body-begin);
    if (resHead == NULL)
    {
        res = NULL;
        goto end;
    }
    /* The third line */
    begin = body; 
    ret = gotoEndtheLine(&body,&length);
    if (ret==0)
    {
        fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
        res = NULL;
        goto end1;
    }
/*    resHead = addData(&resHead,&resHeadLength,begin,body-begin);
    if (resHead == NULL)
    {
        res = NULL;
        goto end;
    }
*/
    /*
     * The Next line represents the boundary
     * We search \r\n
     */
    boundary = body;
    ret = gotoEndtheLine(&body,&length);
    if (ret==0)
    {
        fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
        res = NULL;
        goto end1;
    }
    /*
     * BoundaryLength 
     * -2 = /r/n
     */
    boundaryLength =  (unsigned long) (body - boundary-2);

    resBody = addData(&resBody,&resBodyLength,boundary,body-boundary);
    if (resBody == NULL)
    {
        res = NULL;
        goto end1;
    }
	
    
    while ( length >0 )
    {
    
        /*
         * The next line is Content-disposition and additional information
         * skip it
         */
        headLength = readHeader(body,length);
        resBody = addData(&resBody,&resBodyLength,body,headLength);
        if (resBody == NULL)
        {
            res = NULL;
            goto end1;
        }
        body = body + headLength;
        length = length - headLength;
        /*begin = body; 
        ret = gotoEndtheLine(&body,&length);
        if (ret==0)
        {
            printf("Error in GotoEndtheLigne\nbody : %s\n",body);
            res = NULL;
            goto end2;
        }
        
        resBody = addData(&resBody,&resBodyLength,begin,body-begin);
        if (resBody == NULL)
        {
            res = NULL;
            goto end1;
        }
*/
        /*
         * The next line is a blank (\r\n)
         * skip it
         */
  /*      begin = body;
        ret = gotoEndtheLine(&body,&length);
        if (ret==0)
        {
            printf("Error in GotoEndtheLigne\nbody : %s\n",body);
            res = NULL;
            goto end2;
        }
       
        resBody = addData(&resBody,&resBodyLength,begin,body-begin);
        if (resBody == NULL)
        {
            res = NULL;
            goto end1;
        }
        */

        data = getMultipartData(&body,&length,boundary,boundaryLength,&dataLength);

        if (data == NULL)
        {
            res = NULL;
            goto end2;
        }
        /*
         * Change data
         */
        switch(mode)
        {
            case PERSEUS_CODE :
                {
                 /*
                  * printf("dl : %lu ,DPL : %lu, dP : %s, data %s\n",dataLength, dataPerseusLength,dataPerseus, data);
                  */
                    ret =perseusHTTPencode(PerseusParam,
                            data,
                            dataLength,
                            &dataPerseus,
                            &dataPerseusLength);
                    if (ret==0)
                    {
                        fprintf(stderr,"CRitical Error in pcc_code! Die!\n");
                        MY_FREE(data);
                        res = NULL;
                        goto end2;
                    }
                        break;
                }
            case PERSEUS_DECODE:
                {
                    ret = pcc_decode(PerseusParam,
                             data,
                             dataLength,
                             &dataPerseus,
                             &dataPerseusLength);
                    if (ret == 0)
                    {
                        fprintf(stderr,"Critical Error in pcc_decode! Die!\n");
                        MY_FREE(data);
                        res = NULL;
                        goto end2;
                    }
                    break;
                }
            default :
               MY_FREE(data);
                goto end2;
        }
        /*
         * Add data to the result
         */
        resBody = addData(&resBody,&resBodyLength,dataPerseus,strlen(dataPerseus));
       MY_FREE(dataPerseus);
       dataPerseusLength = 0;
       MY_FREE(data);

        if (resBody == NULL)
        {
            fprintf(stderr,"Error in add Data parseMultipart\n");
            res = NULL;
            goto end1;
        }
        /*
         * Add \r\n
         */
        resBody = addData(&resBody,&resBodyLength,"\r\n",2);
        if (resBody == NULL)
        {
            res = NULL;
            goto end1;
        }

        /* 
         * Add boundary
         */

        resBody = addData(&resBody,&resBodyLength,boundary,boundaryLength);
        if (resBody == NULL)
        {
            res = NULL;
            goto end1;
        }

        /* Skip \r\n */
        begin = body;
        ret = gotoEndtheLine(&body,&length);
        if (ret==0)
        {
            fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
            res = NULL;
            goto end2;
        }
        
        resBody = addData(&resBody,&resBodyLength,begin,body-begin);
        if (resBody == NULL)
        {
            res = NULL;
            goto end1;
        }

    }
    
    /* 
     * The Last step we must re-calculate the length of the data
     * and changes it!
     */
    ret = replaceLength(&resHead, &resHeadLength, resBodyLength);
    
    if (ret == 0)
    {
        goto end2;
    }

    /*
     * Concat the resHead et resBody in res
     */
    res = (char *) malloc (sizeof(char)*(resBodyLength + resHeadLength+1));
    if (res == NULL)
    {
        goto end2;    
    }
    res = strncpy(res,resHead,resHeadLength);
    if (res == NULL)
    {
        fprintf(stderr,"Error in strncpy, parseMultipart\n");
        goto end2;
    }
    res[resHeadLength]='\0';
    res = strncat(res,resBody,resBodyLength);
    if (res == NULL)
    {
        fprintf(stderr,"Error in strncat, parseMultipart\n");
        goto end2;
    }
    res[resBodyLength+resHeadLength]='\0';

end2:
   MY_FREE(resHead);
end1:
   MY_FREE(resBody);
end:
    return res;
}

unsigned char
gotoEqualSep(char ** data, unsigned long * length)
{

    while ( (*length>0) 
            && ((*data)!=NULL) 
            && ((**data) != '=')
          )
    {
        (*data)++;
        (*length)--;
    }

    if ( ((**data) != '=')
       )
    {
        return 0;
    }
    (*data)++;
    (*length)--;
    
    return 1;
}

char *
getDataUrlEncodedOrGet(char ** data, unsigned long * length)
{
    char * res = NULL;
    unsigned long resLength = 0;
    char * begin = (*data);
    while ( (*length>0) 
            && ((*data)!=NULL) 
            && ((**data) != '&')
          )
    {
        (*data)++;
        (*length)--;
    }

    res = addData(&res, &resLength, begin, (*data) -begin); 
    if (res == NULL)
    {
        goto end;
    }

    if ( (*length>0) 
            && ((*data)!=NULL) 
       )
    {
        (*data)++;
        (*length)--;
    }
end:
    return res;
}

char *
parseUrlEncoded(char * body, unsigned long bodyLength, pcc * PerseusParam,  PERSEUS_MODE_t mode)
{
    char * begin = NULL;
    char * res = NULL;
    char * data = NULL;
    char * resHead = NULL;
    char * resBody = NULL;
    unsigned long resBodyLength = 0;
    unsigned long resHeadLength = 0;
    unsigned long dataLength = 0;
    unsigned char ret = 0; 
    unsigned long resLength = 0;
    char * dataPerseus = NULL;
    unsigned long dataPerseusLength = 0;

    if (bodyLength == 0)
    {
        goto end1;
    }
    
    /*
     * The 3 first lines are  the header
     *   1 : Content-Type ... SKIP IT! 
     *   2 : Length : ....... We Must replace the length 
     *   3 : /r/n ............SKIP IT!
     */
    /* First line */
    begin = body; 
    ret = gotoEndtheLine(&body,&bodyLength);
    if (ret==0)
    {
        fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
        res = NULL;
        goto end;
    }

    resHead = addData(&resHead,&resHeadLength,begin,body-begin);
    if (resHead == NULL)
    {
        res = NULL;
        goto end;
    }
    /*
     * The second Line we save it
     */
    begin = body; 
    ret = gotoEndtheLine(&body,&bodyLength);
    if (ret==0)
    {
        fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
        res = NULL;
        goto end1;
    }

    resHead = addData(&resHead,&resHeadLength,begin,body-begin);
    if (resHead == NULL)
    {
        res = NULL;
        goto end;
    }
    /* The third line */
    begin = body; 
    ret = gotoEndtheLine(&body,&bodyLength);
    if (ret==0)
    {
        fprintf(stderr,"Error in GotoEndtheLigne\nbody : %s\n",body);
        res = NULL;
        goto end1;
    }


    while ((bodyLength>0) && (body != NULL))
    {
        /*
         * Search the separator = 
         */
        begin = body;
        ret = gotoEqualSep(&body,&bodyLength);
        if (ret==0)
        {
            fprintf(stderr,"Error in GotoEqualSep\nbody : %s\n",body);
            goto end2;
        }
        resBody = addData(&resBody,&resBodyLength,begin,body-begin);
        if (resBody == NULL)
        {
            /*res = NULL;*/
            goto end2;
        }
        /* 
         * Get Data
         */
        data = getDataUrlEncodedOrGet(&body,&bodyLength);
        if (data == NULL)
        {
            goto end2;
        }
        /*
         * Change data
         */
        dataLength = strlen(data);
        switch(mode)
        {
            case PERSEUS_CODE :
                {
                    ret = perseusHTTPencode(PerseusParam,
                             data,
                             dataLength,
                             &dataPerseus,
                             &dataPerseusLength);
                    if ( ret==0 )
                    {
                        fprintf(stderr,"CRitical Error in pcc_code! Die!\n");
                        MY_FREE(data);
                        goto end2;
                    }
                    break;
                }
            case PERSEUS_DECODE:
                {
                    ret = pcc_decode(PerseusParam,
                             data,
                             dataLength,
                             &dataPerseus,
                             &dataPerseusLength);
                    if (ret == 0)
                    {
                        fprintf(stderr,"CRitical Error in pcc_code! Die!\n");
                        MY_FREE(data);
                        goto end2;
                    }
                    break;
                }
            default :
                MY_FREE(data);
                goto end2;
        }
        /*
         * Add data changed
         */
        resBody = addData(&resBody,&resBodyLength,dataPerseus,strlen(dataPerseus));
        MY_FREE(data);
        MY_FREE(dataPerseus);
        dataLength = 0;
        dataPerseusLength = 0;
        if (resBody == NULL)
        {
            fprintf(stderr,"Error in add Data URLENCODED\n");
            goto end2;
        }
        if (bodyLength>0)
        {
            /*
             * Add &
             */
            resBody = addData(&resBody,&resBodyLength,"&",1);
            if (resBody == NULL)
            {
                goto end2;
            }
        }
    }
    /*
     * printf(" reshead : %s\n readBody : %s\n",resHead,resBody);
     */
    /* 
     * The Last step we must re-calculate the length of the data
     * and changes it!
     */
    ret = replaceLength(&resHead, &resHeadLength, resBodyLength);
    
    if (ret == 0)
    {
        goto end2;
    }

    /*
     * Concat the resHead et resBody in res
     */
    res = (char *) malloc (sizeof(char)*(resBodyLength + resHeadLength+1));
    if (res == NULL)
    {
        goto end2;    
    }
    res = strncpy(res,resHead,resHeadLength);
    if (res == NULL)
    {
        fprintf(stderr,"Error in strncpy, parseMultipart\n");
        goto end2;
    }
    res[resHeadLength]='\0';
    res = strncat(res,resBody,resBodyLength);
    if (res == NULL)
    {
        fprintf(stderr,"Error in strncat, parseMultipart\n");
        goto end2;
    }
    res[resBodyLength+resHeadLength]='\0';
    goto end;

end2:
   MY_FREE(resHead);
end1:
   MY_FREE(resBody);
end:
    return res;
}

char *
parseGet(char * body, unsigned long bodyLength, pcc * PerseusParam,  PERSEUS_MODE_t mode)
{
    char * begin = NULL;
    char * res = NULL;
    char * data = NULL;
    unsigned long dataLength = 0;
    unsigned char ret = 0; 
    unsigned long resLength = 0;
    char * dataPerseus = NULL;
    unsigned long dataPerseusLength = 0;

    if (bodyLength == 0)
    {
	    ret =1;
        goto end;
    }

    while ((bodyLength>0) && (body != NULL))
    {
        /*
         * Search the separator = 
         */
        begin = body;
        ret = gotoEqualSep(&body,&bodyLength);
        if (ret==0)
        {
            fprintf(stderr,"Error in GotoEqualSep\nbody : %s\n",body);
            goto end1;
        }
        res = addData(&res,&resLength,begin,body-begin);
        if (res == NULL)
        {
            /*res = NULL;*/
            goto end1;
        }
        /* 
         * Get Data
         */
        data = getDataUrlEncodedOrGet(&body,&bodyLength);
        if (data == NULL)
        {
            goto end1;
        }
        /*
         * Change data
         */
        dataLength = strlen(data);
        switch(mode)
        {
            case PERSEUS_CODE :
                {
                    ret = perseusHTTPencode(PerseusParam,
                             data,
                             dataLength,
                             &dataPerseus,
                             &dataPerseusLength);
                    if ( ret==0 )
                    {
                        fprintf(stderr,"CRitical Error in pcc_code! Die!\n");
                        MY_FREE(data);
                        goto end1;
                    }
                    break;
                }
            case PERSEUS_DECODE:
                {
                    ret = pcc_decode(PerseusParam,
                             data,
                             dataLength,
                             &dataPerseus,
                             &dataPerseusLength);
                    if (ret == 0)
                    {
                        fprintf(stderr,"CRitical Error in pcc_code! Die!\n");
                        MY_FREE(data);
                        goto end1;
                    }
                    break;
                }
            default :
                MY_FREE(data);
                goto end1;
        }
        /*
         * Add data changed
         */
        res = addData(&res,&resLength,dataPerseus,strlen(dataPerseus));
        MY_FREE(data);
        MY_FREE(dataPerseus);
        dataLength = 0;
        dataPerseusLength = 0;
        if (res == NULL)
        {
            fprintf(stderr,"Error in add Data URLENCODED\n");
            goto end1;
        }
        if (bodyLength>0)
        {
            /*
             * Add &
             */
            res = addData(&res,&resLength,"&",1);
            if (res == NULL)
            {
                goto end1;
            }
        }
    }
    goto end;

end1:
    MY_FREE(res);
end:
    return res;
}


unsigned char
notAdigit(char data)
{

    if (data<'0' || data>'9')
        return 1;

    return 0;

}

unsigned long
getNumber(char ** data, unsigned int * length, unsigned char maxDigit,
            char * paramName)
{
    unsigned long number=0;
    paramName = paramName;

    while (     ((*length)>0) 
            &&  ( (*data) != NULL)
            &&  (maxDigit>0)
          )
    {
        if (notAdigit( (**data)))
           goto end;
        number=10*number+((*(*data))-'0');
        (*data)++;
        (*length)--;
        maxDigit--;
    }
    
  /* 
     fprintf(stderr,"[parsePCC] -> Error in parse %s parameter\n"
            "Debug : {length=%u, data=0x%08x, maxDigit=%u}\n",paramName,*length,(unsigned int)(*data),maxDigit);
    */
    number=0;

end:
    return number;
}

unsigned char 
getBit(char ** data, unsigned int * length, char * paramName)
{
    unsigned char number=0;
    paramName = paramName;
    if ((*length)==0 || (*data) == NULL)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in parse %s parameter\n"
            "Debug : {length=%u, data=0x%08X}\n",paramName,*length,(unsigned int)(*data));
            */
       
        number=2;
        goto end;
    }
    if (notAdigit( (**data)))
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in parse %s parameter\n"
            "Debug : {length=%u, data=0x%08X}\n",paramName,*length,(unsigned int)(**data));
       */
       number=2;
        goto end;
    }
    
    number=(*(*data))-'0';
    (*data)++;
    (*length)--;
end: 
    return number;
}

unsigned char
purgeDelimiter(char **data, unsigned int * length, char * delimiter, unsigned char delimiterLength)
{

    unsigned char i=0;

    while (     (*length>0) 
            &&  ( (*data) != NULL)
            &&  (i<delimiterLength)
          )
    {
        if ((**data) != delimiter[i])
           goto end;
        (*data)++;
        (*length)--;
        i++;
    }
    /* Position on the next digit*/
    return 0;
end:
    return 1;

}
/* Parser : String to PCC Structure
 * in : char * data : perseus parameters
 *      unsigned int length : data length
 * return PCC struct or NULL if a error occurs
 */
pcc *
parsePCC(char * data, unsigned int length)
{
    unsigned int uInt;
    unsigned char maxDigit;
    unsigned char status;
    unsigned long uLong;
    unsigned long ** polynoms;
    unsigned long maxPoly;
    char delimiter[] = "\r\n";
    unsigned char delimiterLength = 2;
    unsigned char k,n;
    unsigned char * matrix;
    unsigned char bit;

    pcc * params = ( pcc *) malloc(sizeof(pcc));

    if (params == NULL)
    {
        fprintf(stderr,"[parsePCC] -> Error Memory for allocate pcc struct\n");
        /*params = NULL;*/
        goto end;
    }

    if (length == 0)
    {
        fprintf(stderr,"[parsePCC] -> Error no data avalaible\n");
        goto end1;
    }

    /* Get N output parameters*/
    /* N parameter has 1 or 2 digits*/

    maxDigit = 3;

    uLong = getNumber(&data,&length,maxDigit,"N");
    if (uLong == 0)
    {
        goto end1;
    }

    status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
    if (status != 0)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
        */
        goto end1;
    }

    if (uLong<NMIN_GEN || uLong>(NMAX_GEN+NMIN_GEN))
    {
        fprintf(stderr,"[parsePCC] -> Error N parameter value : %lu not in interval [%u,%u]\n",
                uLong,NMIN_GEN,NMAX_GEN);
        goto end1;
    }
    /*
     * fprintf(stderr,"[parsePCC] -> N parameter : %lu\n",uLong);
     */
    params->mN = uLong;

    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get K input parameters*/
    /* K parameter has 1 or 2 digits*/

    maxDigit = 3;

    uLong = getNumber(&data,&length,maxDigit,"K");
    if (uLong == 0)
    {
        goto end1;
    }

    status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
    if (status != 0)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
        */
        goto end1;
    }


    if (uLong<KMIN_GEN || uLong>(KMAX_GEN+KMIN_GEN))
    {
        fprintf(stderr,"[parsePCC] -> Error K parameter value : %lu not in interval [%u,%u]\n",
                uLong,KMIN_GEN,KMAX_GEN);
        goto end1;
    }
    /*
     * fprintf(stderr,"[parsePCC] -> K parameter : %lu\n",uLong);
     */
    params->mK = uLong;

    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get M memoryparameters*/
    /* M parameter has 1 or 2 digits*/

    uInt = 0;
    maxDigit = 3;

    uLong = getNumber(&data,&length,maxDigit,"M");
    if (uLong == 0)
    {
        goto end1;
    }

    status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
    if (status != 0)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
        */
        goto end1;
    }

    if (uLong<MIN_CONT || uLong>(MAX_CONT+MIN_CONT))
    {
        fprintf(stderr,"[parsePCC] -> Error M parameter value : %lu not in interval [%u,%u]\n",
                uLong,MIN_CONT,MAX_CONT);
        goto end1;
    }

    /*
     * fprintf(stderr,"[parsePCC] -> M parameter : %lu\n",uLong);
     */
    params->mM = uLong;

    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get polynoms parameters*/
    /* We have n*k polynoms maxPoly=(1<<(M+1))-1;*/

    maxPoly = (1<<(params->mM+1))-1;

    /* Initialisation : Allocate memory for poly*/
    polynoms = (unsigned long **) malloc(sizeof(unsigned long*) * params->mK);

    if (polynoms == NULL)
    {
        fprintf(stderr,"[parsePCC] -> Error Memory for allocate polynoms\n");
        goto end1;
    }

    for (uInt=0; uInt<params->mK; uInt++)
    {
        polynoms[uInt] = (unsigned long *) malloc(sizeof(unsigned long) * params->mN);
        if (polynoms[uInt] == NULL)
        {
            fprintf(stderr,"[parsePCC] -> Error Memory for allocate polynoms\n");
            for(uInt=uInt;uInt>0;uInt--)
               MY_FREE(polynoms[uInt]);
           MY_FREE(polynoms[0]);
            goto end2;
        }
    }
    uInt=0;
    maxDigit=(unsigned char)(log(maxPoly)/log(10))+2;
    /*
      fprintf(stderr,"Polynom MaxDigit : %u\n",maxDigit);
     */
    k=0;
    n=0;
    while (uInt < (params->mN*params->mK))
    {
        uLong = getNumber(&data,&length,maxDigit,"polynoms"); 
        if (uLong == 0)
        {
            fprintf(stderr,"Error to get polynoms\n");
            goto end3;
        }

        status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
        if (status != 0)
        {
            fprintf(stderr,"Error to purge delimiter in PCC parameters string [polynoms]\n");
            /*
            fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                    "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
            */
            goto end3;
        }
        if (uLong<1 || uLong>maxPoly)
        {
            fprintf(stderr,"[parsePCC] -> Error polynom parameter value : %lu not in interval [%u,%lu]\n",
                    uLong,1,maxPoly);
            goto end3;
        }

        /*
         * fprintf(stderr,"[parsePCC] -> polynom[%u][%u]=%lu\n",k,n,uLong);
         */

        uInt++;
        polynoms[k][n] = uLong;
        n++;
        if ( n == params->mN)
        {
            n=0;
            k++;
        }
    }    

    params->mPoly=polynoms;
    
    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get Matrix width parameters*/
    /* Matrix has n*MatWidth bit */

    
    uInt = 0;
    maxDigit = 3;

    uLong = getNumber(&data,&length,maxDigit,"Matrix Width");
    if (uLong == 0)
    {
        goto end3;
    }

    status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
    if (status != 0)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
        */
        goto end3;
    }

    if (uLong<MIN_MATWIDTH || uLong>(MAX_MATWIDTH+MIN_MATWIDTH))
    {
        fprintf(stderr,"[parsePCC] -> Error Matrix width parameter value : %lu not in interval [%u,%u]\n",
                uLong,MIN_MATWIDTH,MAX_MATWIDTH);
        goto end3;
    }

    /*
     * fprintf(stderr,"[parsePCC] -> Matrix width parameter : %lu\n",uLong);
     */
    params->mMatWidth = uLong;

    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get Matrix Depth parameters*/
    /* MatWidth parameter has 1 or 2  or 3 digits (max is N * MatWidth) */

    uInt = 0;
    maxDigit = 4;

    uLong = getNumber(&data,&length,maxDigit,"Matrix Depth");
    if (uLong == 0)
    {
        goto end3;
    }

    status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
    if (status != 0)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
        */
        goto end3;
    }

    if (uLong<1 || uLong>(params->mN*params->mMatWidth))
    {
        fprintf(stderr,"[parsePCC] -> Error Matrix depth parameter value : %lu not in interval [%u,%u]\n",
                uLong,MIN_CONT,MAX_CONT);
        goto end3;
    }

    /*
     * fprintf(stderr,"[parsePCC] -> Matrix depth parameter : %lu\n",uLong);
     */
    params->mMatDepth = uLong;
  
    
    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get Matrix  parameters*/
    /* MatWidth parameter has 1 or 2 digits (max is N * MatWidth) */

    matrix = (unsigned char *) malloc(sizeof(unsigned char)*(params->mN)*(params->mMatWidth));

    if (matrix == NULL)
    {
        fprintf(stderr,"[parsePCC] -> Error Memory for allocate matrix\n");
        goto end3;
    }

    maxDigit=2;
    for (uInt=0;uInt<(params->mN)*(params->mMatWidth);uInt++)
    {
        bit = getBit(&data,&length,"Matrix");
        if (bit != 0 && bit != 1)
        {
            fprintf(stderr,"[parsePCC] -> Error in getBit : %u\n",bit);
            goto end4;
        }

        status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
        if (status != 0)
        {
            /*
            fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                    "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
            */
            goto end4;
        }

        /*
         * fprintf(stderr,"[parsePCC] -> matrix[%u]=%u\n",uInt,bit);
         */
        matrix[uInt] = bit;
            
    }
    
    params->mMatrix = matrix;

    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get NumLine parameters*/
    /* NumLine parameter has 1 digits */

    uInt = 0;
    maxDigit = 2;

    uLong = getNumber(&data,&length,maxDigit,"Matrix Depth");
    /* Can be zero !*/
    /*TODO change error return*/
    if (uLong > 10)
    {
        fprintf(stderr,"Error in get NUmber NUMLINE\n");
        goto end4;
    }

    status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
    if (status != 0)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
        */
        goto end4;
    }

    if (uLong>9)
    {
        fprintf(stderr,"[parsePCC] -> Error NumLine parameter value : %lu not in interval [%u,%u]\n",
                uLong,0,9);
        goto end4;
    }

    /*
     * fprintf(stderr,"[parsePCC] -> NumLine parameter : %lu\n",uLong);
     */
    params->mNumLine = (unsigned char) uLong;
    
    
    /***********************************************************************************/
    /***********************************************************************************/
    /***********************************************************************************/
    /* Get X0 parameters*/
    /* X0 parameter has 64 bits : 19  digits */

    uInt = 0;
    maxDigit = 20;

    uLong = getNumber(&data,&length,maxDigit,"X0");
    if (uLong == 0)
    {
        goto end4;
    }

    status = purgeDelimiter(&data,&length,delimiter,delimiterLength); 
    if (status != 0)
    {
        /*
        fprintf(stderr,"[parsePCC] -> Error in purge delimiter\n"
                "Debug : {length=%u, data=0x%08x}\n",length,(unsigned int)data);
        */
        goto end4;
    }

    if (uLong>(unsigned int)(1<<31))
    {
        fprintf(stderr,"[parsePCC] -> Error X0 parameter value : %lu not in interval [%u,%u]\n",
                uLong,0,1<<31);
        goto end4;
    }

    /*
     * fprintf(stderr,"[parsePCC] -> NumLine parameter : %lu\n",uLong);
     */
    params->mX0 = uLong;
    
    if (length>0)
    {
        fprintf(stderr,"[parsePCC] -> Error more data avalaible\n");
        goto end4;
    }

    goto end;
end4:
   MY_FREE(matrix);
end3:
    for (uInt=0; uInt<params->mK; uInt++)
    {
        MY_FREE(polynoms[uInt]);
    }
end2:
   MY_FREE(polynoms);
end1:
   MY_FREE(params);
end:
    return params;
}

void
addNumber(char ** data, char * number, unsigned char numberLength, unsigned int * add)
{
    unsigned char i;
    (*data) = (char *) realloc(*data,sizeof(char)*((*add)+numberLength+2));
    
    if ((*data) == NULL)
    {
        return;
    }

    for (i=0; i<numberLength;i++)
    { 
        (*data)[(*add)++] = number[i];
    }
    
    (*data)[(*add)++] = '\r';
    (*data)[(*add)++] = '\n';
}

char *
pcc2string(pcc * params)
{
    char * result=NULL;
    unsigned int resultLength=0;
    /* Max is a 64 bits number*/
    char res[64];
    unsigned char base = 10;
    
    unsigned char i,j;

    itoa2(params->mN,res,base);
    addNumber(&result,res,strlen(res),&resultLength);
    
    itoa2(params->mK,res,base);
    addNumber(&result,res,strlen(res),&resultLength);
    
    itoa2(params->mM,res,base);
    addNumber(&result,res,strlen(res),&resultLength);

    
    for (i=0; i< params->mK;i++)
    {
        for (j=0; j< params->mN; j++)
        {
            itoa2(params->mPoly[i][j],res,base);
            addNumber(&result,res,strlen(res),&resultLength);
        }
    }

    itoa2(params->mMatWidth,res,base);
    addNumber(&result,res,strlen(res),&resultLength);
    
    itoa2(params->mMatDepth,res,base);
    addNumber(&result,res,strlen(res),&resultLength);
    
    for (i=0 ; i < (params->mN*params->mMatWidth) ; i++)
    {
        itoa2(params->mMatrix[i],res,base);
        addNumber(&result,res,strlen(res),&resultLength);
    }
    itoa2(params->mNumLine,res,base);
    addNumber(&result,res,strlen(res),&resultLength);
    
    itoa2(params->mX0,res,base);
    addNumber(&result,res,strlen(res),&resultLength);
  

   result = (char *) realloc(result,sizeof(char)*(resultLength+1));
   result[resultLength] = '\0';
    return result;
}


